/**
 * create by zhangpu
 * date:2015年3月2日
 */
package com.acooly.module.openapi.client.provider.fudian;

import com.acooly.core.utils.FreeMarkers;
import com.acooly.core.utils.net.HttpResult;
import com.acooly.module.openapi.client.api.AbstractApiServiceClient;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.exception.ApiClientSocketTimeoutException;
import com.acooly.module.openapi.client.api.exception.ApiServerException;
import com.acooly.module.openapi.client.api.marshal.ApiMarshal;
import com.acooly.module.openapi.client.api.marshal.ApiUnmarshal;
import com.acooly.module.openapi.client.api.message.PostRedirect;
import com.acooly.module.openapi.client.api.transport.Transport;
import com.acooly.module.openapi.client.provider.fudian.domain.FudianNotify;
import com.acooly.module.openapi.client.provider.fudian.domain.FudianRequest;
import com.acooly.module.openapi.client.provider.fudian.domain.FudianResponse;
import com.acooly.module.openapi.client.provider.fudian.exception.FudianProcessingException;
import com.acooly.module.openapi.client.provider.fudian.marshall.FudianNotifyUnmarshall;
import com.acooly.module.openapi.client.provider.fudian.marshall.FudianRedirectPostMarshall;
import com.acooly.module.openapi.client.provider.fudian.marshall.FudianRequestMarshall;
import com.acooly.module.openapi.client.provider.fudian.marshall.FudianResponseUnmarshall;
import com.acooly.module.openapi.client.provider.fudian.utils.HttpServletRequestUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * 上海银行P2P存管ApiService执行器  外部业务禁止使用
 *
 * @author zhangpu
 */
@Slf4j
@Component("fudianApiServiceClient")
public class FudianApiServiceClient
        extends AbstractApiServiceClient<FudianRequest, FudianResponse, FudianNotify, FudianNotify> {


    @Resource(name = "fudianHttpTransport")
    private Transport transport;
    @Resource(name = "fudianRequestMarshall")
    private FudianRequestMarshall requestMarshal;
    @Resource(name = "fudianResponseUnmarshall")
    private FudianResponseUnmarshall responseUnmarshal;
    @Resource(name = "fudianNotifyUnmarshall")
    private FudianNotifyUnmarshall notifyUnmarshal;
    @Resource(name = "fudianRedirectPostMarshall")
    private FudianRedirectPostMarshall fudianRedirectPostMarshall;

    @Autowired
    private OpenAPIClientFudianProperties openAPIClientFudianProperties;

    /**
     * 同步请求
     *
     * @param request
     * @return
     */
    @Override
    public FudianResponse execute(FudianRequest request) {
        try {
            beforeExecute(request);
            String serviceName = FudianConstants.getServiceName(request);
            String url = FudianConstants.getCanonicalUrl(openAPIClientFudianProperties.getGatewayUrl(), serviceName);
            request.setService(serviceName);
            String requestMessage = getRequestMarshal().marshal(request);
            HttpResult result = getTransport().request(requestMessage, url);
            if (result.getStatus() >= HttpStatus.SC_BAD_REQUEST) {
                throw new RuntimeException("HttpStatus:" + result.getStatus());
            }
            FudianResponse t = this.responseUnmarshal.unmarshal(result.getBody(), serviceName);
            afterExecute(t);
            return t;
        } catch (FudianProcessingException pe) {
            log.error("解析响应报文异常：" + pe.getMessage(), pe);
            throw pe;
        } catch (ApiClientSocketTimeoutException ase) {
            log.error("响应超时异常：" + ase.getMessage(), ase);
            throw new FudianProcessingException(ase.getMessage());
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }

    private HttpPost buildHttpPost(String url, String body, ContentType contentType) {
        HttpPost post = new HttpPost(url);
        post.setEntity(new StringEntity(body, contentType));
        return post;
    }

    private ContentType loadContentType(ContentType contentType) {
        if (contentType != null) {
            return contentType;
        }
        return ContentType.create(
                ContentType.APPLICATION_FORM_URLENCODED.getMimeType(), "utf-8");
    }


    @Override
    public String redirectGet(FudianRequest request) {
        PostRedirect postRedirect = redirectPost(request);
        Map<String, Object> templateData = Maps.newHashMap();
        templateData.put("random", RandomStringUtils.randomAlphanumeric(10));
        templateData.put("redirectUrl", postRedirect.getRedirectUrl());
        templateData.put("formDatas", postRedirect.getFormDatas());
        String template =
                "<form accept-charset=\"utf-8\" id=\"redirectForm_${random}\" action=\"${redirectUrl}\" method=\"post\">  \n" +
                        "<#list formDatas?keys as key>\n" +
                        "    <input name=\"${key}\" value='${formDatas[key]?default(\"\")}' type='hidden'/>\n" +
                        "</#list>\n" +
                        "</form>\n" +
                        "<script>document.getElementById(\"redirectForm_${random}\").submit();</script>";
        String fromHtml = FreeMarkers.rendereString(template, templateData);
        return fromHtml;
    }

    @Override
    public PostRedirect redirectPost(FudianRequest request) {
        try {
            // 跳转类接口，设置通知地址
            return fudianRedirectPostMarshall.marshal(request);
        } catch (ApiServerException ose) {
            log.warn("服务器错误:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.warn("客户端异常:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.warn("内部错误:" + e.getMessage(), e);
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }

    public FudianNotify notice(HttpServletRequest request, String serviceKey) {
        try {
            Map<String, String> notifyData = HttpServletRequestUtil.getNoticeDateMap(request);
            FudianNotify notify = getNoticeUnmarshal().unmarshal(notifyData, serviceKey);
            afterNotice(notify);
            log.info("通知报文：{}", JSON.toJSONString(notify));
            return notify;
        } catch (ApiClientException oce) {
            log.warn("客户端:{}", oce.getMessage());
            throw oce;
        } catch (Exception e) {
            log.warn("内部错误:{}", e.getMessage());
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }

    @Override
    protected String getRedirectGateway() {
        return openAPIClientFudianProperties.getGatewayUrl();
    }

    @Override
    protected ApiMarshal<String, FudianRequest> getRequestMarshal() {
        return this.requestMarshal;
    }


    @Override
    protected ApiMarshal<String, FudianRequest> getRedirectMarshal() {
        return null;
    }

    @Override
    protected ApiUnmarshal<FudianNotify, Map<String, String>> getNoticeUnmarshal() {
        return this.notifyUnmarshal;
    }


    protected ApiUnmarshal<FudianResponse, String> getResponseUnmarshalSpec() {
        return this.responseUnmarshal;
    }

    @Override
    protected ApiUnmarshal<FudianResponse, String> getResponseUnmarshal() {
        return null;
    }

    @Override
    protected ApiUnmarshal<FudianNotify, Map<String, String>> getReturnUnmarshal() {
        return this.notifyUnmarshal;
    }

    @Override
    protected Transport getTransport() {
        return this.transport;
    }

    @Override
    public String getName() {
        return FudianConstants.PROVIDER_NAME;
    }


}
